package accs.model.steam;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;

import accs.model.Account;
import accs.model.AccountChecker;
import accs.model.Context;
import accs.model.ExtendedHttpClient;
import accs.model.utils.HeaderUtils;

public class SteamChecker implements AccountChecker {
	
	private static final Pattern GAME_PATTERN=Pattern.compile("<p>([^<>]+)</p>");
	private static final Pattern MONEY_PATTERN=Pattern.compile("<div class=\"accountData price\">([^<>]+)</div>");
	private static final Pattern MAIL_PATTERN=Pattern.compile("<div class=\"\">([^<>]*@[^<>]*)</div>");
	
	public SteamAccount login(String user, String password, Context ctx) throws Exception { System.out.println("CHECK: "+user);
		DefaultHttpClient httpclient=new ExtendedHttpClient(ctx);
		
		RSAPublicKey rsaKey=getRSAKey(httpclient, user);
		
		if(!login(httpclient, user, password, rsaKey)) {
			return null;
		}
		
		return getAccount(httpclient,user,password);
	}
		
	private RSAPublicKey getRSAKey(HttpClient client, String user) throws Exception {	
		HttpPost getKey = new HttpPost("https://store.steampowered.com/login/getrsakey/");
		getKey.setHeader(HeaderUtils.getReferer("https://store.steampowered.com/login/"));
		List<NameValuePair> getK = new ArrayList<NameValuePair>();
		getK.add(new BasicNameValuePair("username",user));
		getKey.setEntity(new UrlEncodedFormEntity(getK,"UTF-8"));
		
		String rsaResponse=EntityUtils.toString(client.execute(getKey).getEntity()); System.out.println(rsaResponse);
		
		JSONObject json=new JSONObject(rsaResponse);
		
		if(json.getBoolean("success")) {
			return new RSAPublicKey(json.getString("publickey_mod"),json.getString("publickey_exp"),json.getLong("timestamp"));
		} else {
			throw new IllegalArgumentException("Unsuccessful request");
		}
	}
	
	private boolean login(HttpClient client, String user, String password, RSAPublicKey key) throws Exception {
		HttpPost post=new HttpPost("https://store.steampowered.com/login/dologin");
		post.setHeader(HeaderUtils.getReferer("https://store.steampowered.com/login/"));
		
		ArrayList<NameValuePair> list=new ArrayList<NameValuePair>();
		list.add(new BasicNameValuePair("rsatimestamp",String.valueOf(key.timestamp)));
		list.add(new BasicNameValuePair("username",user));
		list.add(new BasicNameValuePair("password",encrypt(password,key)));
		post.setEntity(new UrlEncodedFormEntity(list,"UTF-8"));
		String result=EntityUtils.toString(client.execute(post).getEntity()); System.out.println(result);
		
		JSONObject json=new JSONObject(result);
		
		if(json.getBoolean("success") || json.getBoolean("emailauth_needed")) {
			return true;
		} else {
			System.out.println(result);
			return false;
		}
	}
	
	private SteamAccount getAccount(HttpClient client, String user, String pass) throws Exception {
		HttpGet getGames = new HttpGet("https://store.steampowered.com/account/");
		getGames.setHeader(HeaderUtils.getReferer("https://store.steampowered.com/login/"));
		HttpResponse resp=client.execute(getGames);
		BufferedReader br=new BufferedReader(new InputStreamReader(resp.getEntity().getContent(),"utf-8"));
		String line=null;
		
		ArrayList<String> games=new ArrayList<String>();
		String email=null, money=null;
		boolean flag=false;
		
		while((line=br.readLine())!=null) {
			line=line.trim(); //System.out.println(line);
			
			if(line.contains("<div class=\"youraccount_pages\">")) flag=true;
			else if(line.contains("<div class=\"youraccount_page\" id=\"store_transactions\" style=\"display: none;\">")) flag=false;
			else if(flag && line.contains("<p>")) {
				Matcher m=GAME_PATTERN.matcher(line);
				m.find();
				games.add(m.group(1));
				System.out.println("Game-Found: "+m.group(1));
			} else if(MONEY_PATTERN.matcher(line).matches()) {
				Matcher m=MONEY_PATTERN.matcher(line);
				m.find();
				money=m.group(1).replace("&#36;","\u0024").replace("&#8364;","\u20AC"); //dollar+euro
			} else if(MAIL_PATTERN.matcher(line).matches()) {
				Matcher m=MAIL_PATTERN.matcher(line);
				m.find();
				email=m.group(1);
			}
		}
		
		return new SteamAccount(user, email, pass, money, games);
	}
		
	private static class RSAPublicKey {
		public BigInteger modulus;
		public BigInteger encryptionExponent;
		public long timestamp;

		public RSAPublicKey(String modulus_hex, String encryptionExponent_hex, long timestamp){
			this.modulus = new BigInteger( modulus_hex, 16);
			this.encryptionExponent = new BigInteger( encryptionExponent_hex, 16);
			this.timestamp=timestamp;
		}		
	}
	
	private static String encrypt(String data, RSAPublicKey pubkey) throws DecoderException{
		if (pubkey == null) return null;
		BigInteger intData = pkcs1pad2(data,(pubkey.modulus.bitLength()+7)>>3);
		//if(data == null) return null;
		intData = intData.modPow(pubkey.encryptionExponent, pubkey.modulus);
		//if(data == null) return null;
		String str = intData.toString(16);
		return Base64.encodeBase64String(Hex.decodeHex(str.toCharArray()));
	}
	
	private static BigInteger pkcs1pad2(String data, int keysize){
		if(keysize < data.length() + 11){
			return null;
		}
		byte[] buffer = new byte[keysize];
		int i = data.length() -1;
		while(i >= 0 && keysize > 0)
		buffer[--keysize] = (byte) ((CharSequence) data).charAt(i--);
		buffer[--keysize] = 0;
		while(keysize > 2)
		buffer[--keysize] = (byte) (Math.floor(Math.random()*254) + 1);
		buffer[--keysize] = 2;
		buffer[--keysize] = 0;
		return new BigInteger(buffer);
	}

//	public Account login(String user, String password) throws Exception {
//		return login(user,password,);
//	}

	public String getName() {
		return "steam";
	}

	public URL getImage() {
		return SteamChecker.class.getResource("/accs/gui/img/steam.png");
	}
}
